package com.lwl.office.dcconverter.converter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.lwl.office.dcconverter.cons.DCDocumentEnum;
import com.lwl.office.dcconverter.cons.DCTableCellValignEnum;
import com.lwl.office.dcconverter.element.*;
import com.lwl.office.dcconverter.style.DCParagraphStyle;
import com.lwl.office.dcconverter.style.DCRunStyle;
import com.lwl.office.dcconverter.style.DCTableCellStyle;
import com.lwl.office.webeditor.ParserContext;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DCHtmlConverter extends Converter{
    private static final Log log = LogFactory.get();
    private final static DCDocumentEnum documentType = DCDocumentEnum.HTML;
    //段落标签
    private static List<String> hTags = Arrays.asList("h1","h2","h3","h4","h5","h6","h7");
    @Override
    protected DCRoot parse() {
        DCRoot root = new DCRoot();
        String html = (String) getInput();
        html = html.replace("&nbsp:"," ");
        html = html.replaceAll("\\n","");
        html = html.replaceAll("\\p{Cntrl}","");
        Element element = Jsoup.parse(html);
        List<Node> nodes = element.getElementsByTag("body").get(0).childNodesCopy();
        parseNode(nodes,root);
        return root;
    }

    /**
     * 根据node的tagname解析node
     * @param nodes
     * @param parent
     */
    private void parseNode(List<Node> nodes,AbsDCElement parent){
         for (Node node : nodes) {
            AbsDCElement current = null;
            if(node instanceof Element){
                Element e = (Element) node;
                if(hTags.contains(e.tagName())){
                    String lvl = e.tagName().replace("h", "");
                    current = parseParagraph(e,new DCHeader(Integer.parseInt(lvl)));
                }else{
                    switch (e.tagName()){
                        case "table":
                            current = parseTable(e);
                            break;
                        case "ul":
                            current = parseList(e,new DCUnorderedList());
                            break;
                        case "ol":
                            current = parseList(e,new DCOrderedList());
                            break;
                        case "p":
                            current = parseParagraph(e,new DCParagraph());
                            break;
                        case "span":
                            current = parseRun(e,new DCRun());
                            break;
                        case "a":
                            DCLinkRun dclink = new DCLinkRun();
                            dclink.setHref(e.hasAttr("href")?e.attr("href"):null);
                            current = parseRun(e,dclink);
                            break;
                        case "img":
                            current = parseImg(e);
                            break;
                        case "strong":
                            DCRun dcRun = new DCRun();
                            DCRunStyle dcrs = new DCRunStyle(documentType);
                            dcrs.setBold(true);
                            dcRun.setStyle(dcrs);
                            current = parseRun(e,dcRun);
                            break;
                        case "em":
                            dcRun = new DCRun();
                            dcrs = new DCRunStyle(documentType);
                            dcrs.setItalic(true);
                            dcRun.setStyle(dcrs);
                            current = parseRun(e,dcRun);
                            break;
                        case "sup":
                            dcRun = new DCRun();
                            dcrs = new DCRunStyle(documentType);
                            dcrs.setSuperscript(true);
                            dcRun.setStyle(dcrs);
                            current = parseRun(e,dcRun);
                            break;
                        case "sub":
                            dcRun = new DCRun();
                            dcrs = new DCRunStyle(documentType);
                            dcrs.setSubscript(true);
                            dcRun.setStyle(dcrs);
                            current = parseRun(e,dcRun);
                            break;
                    }
                }
                if (current!=null){
                    parent.addChild(current);
                    parseNode(node.childNodes(),current);
                }else{
                    log.warn("未知的节点[{}]",e.tagName());
                }
            }else if(node instanceof TextNode){
                DCRun run = new DCRun();
                run.setText(((TextNode) node).text());
                parent.addChild(run);
            }else{
                log.warn("未知的节点类型[{}]",node.getClass());
            }
        }
    }

    /**
     * 解析list
     * @param e
     * @param list
     * @param <T>
     * @return
     */
    private <T extends DCUnorderedList> T parseList(Element e, T list) {
        Elements lis = e.getElementsByTag("li");
        for (Element li : lis) {
            DCList dcList = list.addList();
            List<Node> nodes = li.childNodes();
            nodes = nodes.stream().filter(t -> t instanceof Element).collect(Collectors.toList());
            parseNode(nodes,dcList);
        }
        return list;
    }
    /**
     * 解析img标签
     * @param e
     * @return
     */
    private DCPic parseImg(Element e) {
        DCPic dcpic = new DCPic();
        dcpic.setUrl(e.attr("src"));
        return dcpic;
    }

    /**
     * 解析run标签
     * @param e
     * @param dcr
     * @param <T>
     * @return
     */
    private <T extends DCRun> T parseRun(Element e, T dcr) {
        if(dcr.getStyle()==null){
            dcr.setStyle(new DCRunStyle(documentType));
        }
        DCRunStyle dcrs = (DCRunStyle) dcr.getStyle();
        //提取样式信息
        if(e.hasAttr("style")){
            String style = e.attr("style").trim();
            if(StrUtil.isNotBlank(style)){
                String[] styles = style.split(";");
                for (String s : styles) {
                    String[] styleKV = s.split(":");
                    String k = styleKV[0].trim();
                    String v = styleKV[1].trim();
                    switch (k){
                        case "font-family":
                            if(v.contains(",")){
                                v = v.split(",")[0].trim();
                            }
                            dcrs.setFontFamily(v);
                            break;
                        case "font-size":
                            dcrs.setFontSize(v);
                            break;
                        case "text-decoration":
                            if (v.contains("line-through")){
                                dcrs.setLineThrough(true);
                            }
                            if(v.contains("underline")){
                                dcrs.setUnderline(true);
                            }
                            break;
                        case "font-weight":
                            dcrs.setBold(v.contains("bold"));
                            break;
                        case "color":
                        case "background-color":
                            dcrs.setColor(v.contains("#")?v:null);
                            break;
                        case "border":
                            dcrs.setBorder(true);
                            break;
                    }
                }
            }
        }
        return dcr;
    }

    private DCTable parseTable(Element e) {
        Element tbody = e.getElementsByTag("tbody").get(0);
        Elements trs = tbody.getElementsByTag("tr");
        int rowNum = trs.size();
        int colNum = 0;
        Elements tds = trs.get(0).getElementsByTag("td");
        for (Element td : tds) {
            if(td.hasAttr("colspan")){
                colNum += Integer.parseInt(td.attr("colspan"));
            }else{
                colNum++;
            }
        }
        DCTable dct = new DCTable(rowNum,colNum);
        int flag = 1;
        for (int i = 0; i < trs.size(); i++) {
            Element tr = trs.get(i);
            tds = tr .getElementsByTag("td");
            DCTableRow dctr = dct.addRow();
            int[] cellMap = dct.getMergeMap()[i];
            for (int j = 0,col=0; j < tds.size(); j++) {
                Element td = tds.get(j);
                DCTableCell cell = dctr.addCell();
                //从col开始找第一个没占用的格子
                while (col<colNum && cellMap[col]!=0){
                    col++;
                }
                //单元格合并解析
                int f = flag++;
                cellMap[col]=f;
                //行合并
                int rowspan = td.hasAttr("rowspan")?Integer.parseInt(td.attr("rowspan")):1;
                //列合并
                int colspan = td.hasAttr("colspan")?Integer.parseInt(td.attr("colspan")):1;
                for (int r = 0; r < rowspan; r++) {
                    for (int c = 0; c < colspan; c++) {
                        dct.getMergeMap()[i+r][col+c]=f;
                    }
                }
                col+=colspan;

                //解析样式
                DCTableCellStyle dctbcs = new DCTableCellStyle(documentType);
                cell.setStyle(dctbcs);
                if(td.hasAttr("align")){
                    String align = td.attr("align");
                    switch (align){
                        case "middle":
                            dctbcs.setValign(DCTableCellValignEnum.CENTER);
                            break;
                        case "top":
                            dctbcs.setValign(DCTableCellValignEnum.TOP);
                            break;
                        case "bottom":
                            dctbcs.setValign(DCTableCellValignEnum.BOTTOM);
                    }
                }

                //解析子节点
                List<Node> nodes = td.childNodes();
                parseNode(nodes,cell);
            }
        }
        return dct;
    }
    /**
     * 解析段落节点基础信息
     * @param <T>
     * @param e
     * @param dcp
     * @return
     */
    private <T extends DCParagraph> T parseParagraph(Element e, T dcp) {
        //解析段落样式
        DCParagraphStyle dcps = new DCParagraphStyle(documentType);
        dcp.setStyle(dcps);
        if(e.hasAttr("style")){
            String style = e.attr("style").trim();
            if(StrUtil.isNotBlank(style)){
                String[] styleArr = style.split(";");
                for (String styleItem : styleArr) {
                    String[] styleKV = styleItem.split(":");
                    String k = styleKV[0].trim();
                    String v = styleKV[1].trim();
                    switch (k){
                        case "text-align":
                            dcps.setAlign(v);
                            break;
                        case "text-indent":
                            dcps.setInd(v);
                            break;
                        case "margin-bottom":
                            dcps.setSpacingBefore(v);
                            break;
                        case "margin-top":
                            dcps.setSpacingAfter(v);
                            break;
                        case "line-height":
                            dcps.setLineHeight(v);
                            break;
                        default:
                            log.warn("未解析的style[]",k);
                    }
                }
            }
        }
        return dcp;
    }
    @Override
    protected Object out(DCDocumentEnum type) {
        switch (type){
            case HTML:
                return getRoot().toHtml();
            case WORD:
                ParserContext context = new ParserContext();
                getRoot().parseWord(context);
                return context.getWord();
        }
        return null;
    }
}
